/**
 *  TextMash - simple IDE for Clojure
 *  
 *  Copyright (C) 2010 Aleksander Naszko
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package pl.olek.clojure;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resources;

import pl.olek.textmash.Support;

/**
 * 
 * @author anaszko
 */
public class Configuration {

	Map<String, String[]> values = Collections.emptyMap();
	
	public static synchronized String getName(){
		if (names == null) {
			names = new Configuration(
					Support.getInstance().getId()).get("terminal-names");
		}
		return names[namesIndex++ % names.length];
	}
	
	static int namesIndex;
	
	static String [] names;

//	public static void main(String[] args) {
//
////		System.out.println(System.getProperties().toString());
//		
//		Properties params = new Properties();
////		params.put("CP", "java.class.path");
////		params.put("port", "12345");
//
//		Configuration cfg = new Configuration("mac", params);
//
//		System.out.println(Arrays.toString(cfg.get("clojure")));
//	}

	public String[] get(String name) {
		return (String[]) values.get(name);
	}

	public String getString(String name) {
		return values.get(name)[0];
	}

	public Number getNumber(String name) {
		return new Double(values.get(name)[0]);
	}

	public Configuration() {
	}

	public Configuration(String system, Properties params) {
		load(system, params);
	}
	public Configuration(String system) {
		load(system, null);
	}
	public static File getConfigurationSource(String system) {
		try {
			File f = new File("." + system + ".properties");
			if (f.exists()) {
				return f;
			} else {
				InputStream input = Resources.class
						.getResourceAsStream("/pl/olek/clojure/" + system
								+ ".properties");
				FileOutputStream out = new FileOutputStream(f);
				Stream.redirect(input, out).join();
				out.close();
				input.close();
				return f;
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void load(String system, Properties params) {
		try {
			final LinkedHashMap<Object, Object> vv = 
				new LinkedHashMap<Object, Object>();
			Properties raw = new Properties() {

				private static final long serialVersionUID = 1L;

				@Override
				public synchronized Object put(Object key, Object value) {
//					System.out.println("GOTT " + key);
					vv.put(key, value);
					return null; //super.put(key, value);
				}
				
			};
			File f = new File("." + system + ".properties");
			if (f.exists()) {
				FileInputStream fi = new FileInputStream(f);
				raw.load(fi);
				fi.close();
			} else {
				raw.load(Resources.class
						.getResourceAsStream("/pl/olek/clojure/" + system
								+ ".properties"));
			}
			values = new TreeMap<String, String[]>();
			for (Entry<Object, Object> v : vv.entrySet()) {
//				System.out.println("Found " + v.getKey().toString());
				values.put(v.getKey().toString(), get(v.getValue().toString(),
						params, values));
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static String[] get(String value, Properties params, Map<String, String[]> val) {
		try {

			LinkedList<String> list = new LinkedList<String>();

			boolean escaped = false;
			StringBuilder buf = new StringBuilder();
			boolean had = false;
			for (int i = 0; i < value.length(); ++i) {
				char c = value.charAt(i);
				if (c == '"') {
					had = true;
					if (escaped) {
						escaped = false;
						list.add(buf.toString());
						buf = new StringBuilder();
					} else {
						escaped = true;
					}

				} else if (c == '\\') {
					buf.append(value.charAt(++i));
					continue;
				} else if (escaped) {
					buf.append(c);
				}
			}
			if (!had) {
				list.add(value);
			}

			ListIterator<String> it = list.listIterator();
			Pattern pat = Pattern.compile("\\$\\{([\\.\\_\\-\\d\\w]+)\\}");
			while (it.hasNext()) {
				String str = it.next();
				
				Matcher mat = pat.matcher(str);
				//int i = 0;
				while(mat.find()) {
					
					String what = mat.group(1);
//					System.out.println("Found " + what);
					Object v = null;
					if (params != null) {
						v = params.get(what);
					}
					if (v == null) {
//						int z = list.indexOf(what);
						v = val.get(what);
						
						if (v != null) {
							v = ((String[])v)[0];
							
						} else {
							v = System.getenv(what);
							if (v == null) {
								v = System.getProperty(what);
							}
						}
					}
					
					if (v != null) {
						String s = v.toString();
						s = s.replaceAll("\\\\", "\\\\\\\\");
//						System.out.println("Found start " + s);
						str = mat.replaceFirst(s);
//						System.out.println("Found mid " + str);
						mat = pat.matcher(str);
					}
//					i = mat.end();
				}
				
//				System.out.println("Found end " + str);
//				System.out.println("Found end");

//				for (Entry<Object, Object> e : params.entrySet()) {
//					str = str.replaceAll("\\$\\{" + e.getKey() + "\\}", e
//							.getValue().toString());
//				}
				it.set(str);
			}

			return list.toArray(new String[list.size()]);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}
